<?php
App::uses ( 'Component', 'Controller' );
class Hungdv7SudokuComponent extends Component {
	
	public function __construct(){
		
	}
	
	public function checkSudoku($sudokuArray) {
		$isValidSudoku = '';
		$count = 0;
		// arrays store rows, columns and boxes
		$rows = array ();
		$columns = array ();
		$boxes = array ();
		
		// check the array quantity
		// if the quantity of arrays less than 9 or the quantity of elements in array less than 9 ==> fail
		if ($this->checkQuantity ( $sudokuArray, 9 )) {
			foreach ( $sudokuArray as $sudokuRow ) {
				$count ++;
				if (! $this->checkQuantity ( $sudokuRow, 9 )) {
					$isValidSudoku = 'Invalid quantity';
				}
			}
		} else {
			$isValidSudoku = 'Invalid quantity';
		}
		
		// split the sodokuArray into $rows, $columns and $boxes
		for($rowIndex = 0; $rowIndex < 9; $rowIndex ++) {
			$row = array ();
			$column = array ();
			
			for($columnIndex = 0; $columnIndex < 9; $columnIndex ++) {
				$count ++;
				
				$box = array ();
				// create a row
				array_push ( $row, $sudokuArray [$rowIndex] [$columnIndex] );
				// create a column
				array_push ( $column, $sudokuArray [$columnIndex] [$rowIndex] );
				
				// push cell to box
				// check the offset whether it is top-left of 9 boxes
				if ((($rowIndex + 3) % 3 == 0) && (($columnIndex + 3) % 3 == 0)) {
					$t_rowIndex = $rowIndex;
					$t_columnIndex = $columnIndex;
					
					// create the array{3,3} according to the offset
					array_push ( $box, $sudokuArray [$t_rowIndex] [$t_columnIndex] );
					array_push ( $box, $sudokuArray [$t_rowIndex] [$t_columnIndex + 1] );
					array_push ( $box, $sudokuArray [$t_rowIndex] [$t_columnIndex + 2] );
					$t_columnIndex = $columnIndex;
					
					array_push ( $box, $sudokuArray [$t_rowIndex + 1] [$t_columnIndex] );
					array_push ( $box, $sudokuArray [$t_rowIndex + 1] [$t_columnIndex + 1] );
					array_push ( $box, $sudokuArray [$t_rowIndex + 1] [$t_columnIndex + 2] );
					$t_columnIndex = $columnIndex;
					
					array_push ( $box, $sudokuArray [$t_rowIndex + 2] [$t_columnIndex] );
					array_push ( $box, $sudokuArray [$t_rowIndex + 2] [$t_columnIndex + 1] );
					array_push ( $box, $sudokuArray [$t_rowIndex + 2] [$t_columnIndex + 2] );
					
					// push created box to $boxes array
					array_push ( $boxes, $box );
				}
			}
			
			// push row to $rows array
			array_push ( $rows, $row );
			// push column to $columns array
			array_push ( $columns, $column );
		}
		
		// check the logic of sudoku
		// check through 9 rows => check through 9 array
		foreach ( $rows as $key => $row ) {
			$count ++;
			if (! $this->checkSudokuRowValid ( $row )) {
				$isValidSudoku .= 'Row ' . ($key + 1) . ' invalid ';
				break;
			}
		}
		
		// check through 9 columns => check through each index in 9 array
		foreach ( $columns as $key => $column ) {
			$count ++;
			if (! $this->checkSudokuRowValid ( $column )) {
				$isValidSudoku .= '; Column ' . ($key + 1) . ' invalid ';
				break;
			}
		}
		
		// check through 9 box => check the boxes according to [0,0], [0,3], [0,6], [3,0], [3,3], [3,6], [6,0], [6,3], [6,6]
		foreach ( $boxes as $key => $box ) {
			$count ++;
			if (! $this->checkSudokuRowValid ( $box )) {
				$isValidSudoku .= '; Box ' . ($key + 1) . ' invalid ';
				break;
			}
		}
		
		/*
		 * echo 'Rows: <pre>'; print_r($rows); echo '</pre>'; echo 'Columns: <pre>'; print_r($columns); echo '</pre>'; echo 'Boxes: <pre>'; print_r($boxes); echo '</pre>'; exit;
		 */
		
		if ($isValidSudoku == "") {
			$isValidSudoku = 'Count=' . $count . " Sudoku table is valid ";
		}
		return $isValidSudoku;
	}
	public function checkSudoku1($sudokuArray) {
		$count = 0;
		$isValidSudoku = "";
		// check the array quantity
		// if the quantity of arrays less than 9 or the quantity of elements in array less than 9 ==> fail
		if ($this->checkQuantity ( $sudokuArray, 9 )) {
			
			for($rowIndex = 0; $rowIndex < 9; $rowIndex ++) {
				if (! $this->checkQuantity ( $sudokuArray [$rowIndex], 9 )) {
					$isValidSudoku = 'Not Enough Columns';
					break;
				} else {
					
					$row = array ();
					$column = array ();
					
					for($columnIndex = 0; $columnIndex < 9; $columnIndex ++) {
						$count ++;
						
						$box = array ();
						// create a row
						array_push ( $row, $sudokuArray [$rowIndex] [$columnIndex] );
						// create a column
						array_push ( $column, $sudokuArray [$columnIndex] [$rowIndex] );
						
						// push cell to box
						// check the offset whether it is top-left of 9 boxes
						if ((($rowIndex + 3) % 3 == 0) && (($columnIndex + 3) % 3 == 0)) {
							$t_rowIndex = $rowIndex;
							$t_columnIndex = $columnIndex;
							
							// create the array{3,3} according to the offset
							array_push ( $box, $sudokuArray [$t_rowIndex] [$t_columnIndex] );
							array_push ( $box, $sudokuArray [$t_rowIndex] [$t_columnIndex + 1] );
							array_push ( $box, $sudokuArray [$t_rowIndex] [$t_columnIndex + 2] );
							$t_columnIndex = $columnIndex;
							
							array_push ( $box, $sudokuArray [$t_rowIndex + 1] [$t_columnIndex] );
							array_push ( $box, $sudokuArray [$t_rowIndex + 1] [$t_columnIndex + 1] );
							array_push ( $box, $sudokuArray [$t_rowIndex + 1] [$t_columnIndex + 2] );
							$t_columnIndex = $columnIndex;
							
							array_push ( $box, $sudokuArray [$t_rowIndex + 2] [$t_columnIndex] );
							array_push ( $box, $sudokuArray [$t_rowIndex + 2] [$t_columnIndex + 1] );
							array_push ( $box, $sudokuArray [$t_rowIndex + 2] [$t_columnIndex + 2] );
							
							// push created box to $boxes array
							if (! $this->checkSudokuRowValid ( $box )) {
								$isValidSudoku .= '; Box  invalid ';
								break;
							}
							
							/* array_push ( $boxes, $box ); */
						}
					}
					
					// check each row is valid
					if (! $this->checkSudokuRowValid ( $row )) {
						$isValidSudoku .= '; Row  invalid ';
						break;
					}
					
					// check each column
					if (! $this->checkSudokuRowValid ( $column )) {
						$isValidSudoku .= '; Columns  invalid ';
						break;
					}
				}
			}
		} else {
			$isValidSudoku = 'Not Enough Rows';
		}
		
		exit ();
		
		if ($isValidSudoku == "") {
			$isValidSudoku = "Sudoku table is valid";
		}
		
		$isValidSudoku = 'Count=' . $count . " " . $isValidSudoku;
		return $isValidSudoku;
	}
	
	
	
	public function checkSudoku2($sudokuArray) {
		$count = 0;
		$isValidSudoku = "";
		$boxes = array ();
		$columns = array ();
		$rows = array();
		
		for($rowIndex = 0; $rowIndex < count ( $sudokuArray ); $rowIndex ++) {
			
			// push row into rows array
			array_push ( $rows, $sudokuArray [$rowIndex]);
			
			for($columnIndex = 0; $columnIndex < count ( $sudokuArray [$rowIndex] ); $columnIndex ++) {
				$count ++;
				$box = array ();
				
				
				// push cell to each column
				if (! array_key_exists ( $columnIndex, $columns ))
					$columns [$columnIndex] = array ();
				array_push ( $columns [$columnIndex], $sudokuArray [$rowIndex] [$columnIndex] );
				
				// push cell to each box
				// check the offset whether it is top-left of 9 box
				$boxIndex = ( int ) (floor ( $rowIndex / 3 ) * 10 + floor ( $columnIndex / 3 ));
				
				if (! array_key_exists ( $boxIndex, $boxes ))
					$boxes [$boxIndex] = array ();
				array_push ( $boxes [$boxIndex], $sudokuArray [$rowIndex] [$columnIndex] );
			}
		}
		
		foreach ( $rows as $key => $row ) {
			$count ++;
			if (! $this->checkSudokuRowValid ( $row )) {
				$isValidSudoku .= 'Rows ' . ($key + 1) . ' invalid ';
				break;
			}
		}
		
		foreach ( $columns as $key => $column ) {
			$count ++;
			if (! $this->checkSudokuRowValid ( $column )) {
				$isValidSudoku .= '; Columns ' . ($key + 1) . ' invalid ';
				break;
			}
		}
		
		foreach ( $boxes as $key => $box ) {
			$count ++;
			if (! $this->checkSudokuRowValid ( $box )) {
				$isValidSudoku .= '; Box ' . ($key + 1) . ' invalid ';
				break;
			}
		}
		//echo 'use foreach <br/>';
		
		// check with array_map
		/*
		 * $mapCheckedBoxes = array_map('Hungdv7SudokuComponent::checkSudokuRowValid', $boxes); foreach ( $mapCheckedBoxes as $key => $box ) { $count ++; if (! $box ) { $isValidSudoku .= '; Box ' . ($key + 1) . ' invalid '; break; } } echo 'use array_map <br/>';
		 */
		
		/* echo 'Box: <pre>';
		print_r ( $boxes );
		echo '</pre>'; 
		exit (); */
		
		if ($isValidSudoku == "") {
			$isValidSudoku = "Sudoku table is valid";
		}
		
		//$isValidSudoku = 'Count=' . $count . " " . $isValidSudoku;
		return $isValidSudoku;
	}
	private function checkQuantity($array, $compareTo) {
		if (count ( $array ) != $compareTo) {
			return false;
		} else {
			return true;
		}
	}
	
	
	private function checkSudokuRowValid($checkArray) {
		$temp = "";
		
		// check if array contain 9 elements
		if (count ( $checkArray ) != 9) {
			echo 'not enough <br/>';
			return false;
		}
		
		// check array unique
		$checkArray = array_unique ( $checkArray );
		// check if array contain 9 elements after remove duplicated
		if (count ( $checkArray ) != 9) {
			echo 'duplicated <br/>';
			return false;
		}
		
		/* if (! ctype_digit ( implode ( '', $checkArray ) )) {
			echo 'have string <br/>';
			return false;
		} */
		
		$intArray = array_filter($checkArray, 'is_int');
		if(count($intArray) != 9){
			echo 'not int <br/>';
			return false;
		}
		
		// check in range 1-9
		// check min = 1
		if (min ( $checkArray ) != 1) {
			echo 'range, 1 <br/>';
			return false;
		}
		// check max = 9
		if (max ( $checkArray ) != 9) {
			echo 'range, 9 <br/>';
			return false;
		}
		
		return true;
	}
}